#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<time.h>
#include<gmp.h>

#define iteracoes 23

  //algumas inicializacoes basicas
  unsigned long inteirob0;
  double doublea0 = 0.25;
  unsigned long bitsdeprecisao;
  int i = 0;
  int fimprog = 0; 
  //sao usados vetores para simular a ideia de produtor e consumidor
  //ideia que foi vista em sistemas operacionais I
  pthread_t calcab,calct,calcp,calcpi;
  mpf_t a[iteracoes];
  mpf_t b[iteracoes];
  int abper[iteracoes];
  mpf_t t[iteracoes];
  int tper[iteracoes];
  mpf_t p[iteracoes];
  int pper[iteracoes];
  mpf_t pi[iteracoes];
  int dieprog = 0;
  time_t inicio,fim;//tempos de inicio e fim do programa
  //esses vetores sao de vital importancia para que a thread
  //exexute sem precisar esperar as demais caso ela nao dependa
  //das outras

// Essa funcao serve apenas para fazer as threads dormirem
int mywait(int timeInSec, int numero){
//mutexes foram usadas para cada thread nao precisasse disputar esse
//recurso, logo foi criado um mutez para cada thread, que tem a funcao
//de servir como argumento apra a funcao de timedwait que paramos
//de usar pois ela era um cancelation point, e acava matando as 
//threads sem que percebessemos
pthread_mutex_t mutexdesleep1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexdesleep2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexdesleep3 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexdesleep4 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexdesleep5 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t auxmut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condsleep1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t condsleep2 = PTHREAD_COND_INITIALIZER;
pthread_cond_t condsleep3 = PTHREAD_COND_INITIALIZER;
pthread_cond_t condsleep4 = PTHREAD_COND_INITIALIZER;
pthread_cond_t condsleep5 = PTHREAD_COND_INITIALIZER;
pthread_cond_t auxcond = PTHREAD_COND_INITIALIZER;
if (numero == 1){ auxmut = mutexdesleep1; auxcond = condsleep1;}
if (numero == 2){ auxmut = mutexdesleep2; auxcond = condsleep2;}
if (numero == 3){ auxmut = mutexdesleep3; auxcond = condsleep3;}
if (numero == 4){ auxmut = mutexdesleep4; auxcond = condsleep4;}
if (numero == 5){ auxmut = mutexdesleep5; auxcond = condsleep5;}
struct timespec timeToWait;
struct timeval now;
int rt;
gettimeofday(&now,NULL);
timeToWait.tv_nsec = now.tv_usec*100 + now.tv_usec*500*timeInSec;
pthread_mutex_lock(&auxmut);
rt = pthread_cond_timedwait(&auxcond, &auxmut, &timeToWait);
pthread_mutex_unlock(&auxmut);
return rt;//retorna o erro da funcao, que esperamos que fique sendo
//ETIMEDOUT
}
//funcao que calcula a e b de gauss
void* calculaab( void* ponteironulo ){
  
  for(i = 0; i<iteracoes; i++){
    mpf_clear(a[i]);
    mpf_clear(b[i]);
    mpf_init(a[i]);
    mpf_init(b[i]);
    abper[i] = 0;
  }
  mpf_t temp;
  mpf_t temp2;
  int i;
  mpf_init(temp);
  mpf_init(temp2);
  mpf_set_ui(a[0],1);
  mpf_set_ui(b[0],2);
  mpf_sqrt(b[0],b[0]);
  mpf_div_ui(b[0],b[0],2);
  abper[0] = 1;
  for(i = 0; i<iteracoes - 1 ; i++){
    mpf_add(temp,a[i],b[i]);
    mpf_div_ui(temp,temp,2);
    mpf_set( a[i+1], temp );
    mpf_mul(temp2,a[i],b[i]);
    mpf_sqrt(b[i+1],temp2);
    abper[i+1] = 1;
  }
  mpf_clear(temp);
  mpf_clear(temp2);  
 }
//calculo de t
void* calculat( void* ponteironulo ){
  
  mpf_t temp;
  mpf_t temp2;
  int i;
  for(i = 0; i<iteracoes; i++){
    mpf_init(t[i]);
    tper[i] = 0;
  }
  mpf_init(temp);
  mpf_init(temp2);
  mpf_set_d(t[0],0.25);
  tper[0] = 1;
  for(i = 0; i<iteracoes - 1 ; i++){
    while ( abper[i+1] == 0 ){//esses whiles vazios servem
    }//como se fossem um sleep, e esse foi o unico jeito
    mpf_sub(temp,a[i],a[i+1]);//que garantia o bom funcionamento
    mpf_pow_ui(temp,temp,2);//sem cancelar as threads gerando erro
    while ( pper[i] == 0 ){//ou cirando muitas threads e deixando
    }//programa mais lento
    mpf_mul(temp,temp,p[i]);
    mpf_sub(t[i+1],t[i],temp);
    tper[i+1] = 1;
  }
  mpf_clear(temp);
  mpf_clear(temp2);  
}
//calculo de p
void* calculap( void* ponteironulo ){
  
  mpf_t temp;
  mpf_t temp2;
  int i;
  for(i = 0; i<iteracoes; i++){
    mpf_clear(p[i]);
    mpf_init(p[i]);
    pper[i] = 0;
  }
  mpf_set_d(p[0],1.0);
  pper[0] = 1;
  for(i = 0; i<iteracoes - 1; i++){
    mpf_mul_ui(p[i+1],p[i],2);
    pper[i+1] = 1;
  }
  mpf_clear(temp);
  mpf_clear(temp2);  
}
//calculo de pi
void* calculapi( void* ponteironulo ){
  
  mpf_t temp;
  mpf_t temp2;
  int i;
  for(i = 0; i<iteracoes; i++){
    mpf_clear(pi[i]);
    mpf_init(pi[i]);
  }
  mpf_clear(temp);
  mpf_clear(temp2);
  mpf_init(temp);
  mpf_init(temp2);
  
  for(i = 0; i<iteracoes ; i++){
    while ( (abper[i] ==  0) ){
    }
    printf("\nabper foi finalizado em pi");
    mpf_add(temp, a[i], b[i]);
    mpf_pow_ui(temp, temp, 2);
    while ( tper[i] == 0){
    }
    printf("\nabper e tper calculados pi");  
    mpf_mul_ui(temp2,t[i],4);
    mpf_div(pi[i],temp,temp2);
    printf("iteracao %d",i+1);
    gmp_printf("\nEsse eh o valor de pi %.10000000Ff - pi foi impresso\n ",pi[i]); 
  }
  mpf_clear(temp);
  mpf_clear(temp2);
  dieprog = 1;
}

int main(){
  
  inicio = time(NULL);
  mpf_set_default_prec(33219280);//essa precisao ja foi explicada
  pthread_mutex_t mtxsleep = PTHREAD_MUTEX_INITIALIZER;
  pthread_cond_t mtxcondsleep = PTHREAD_COND_INITIALIZER;
  int i;
  int create1 = 1;// essas ajudam a garantir que as threads serao
  int create2 = 1;//criadas pois a criacao da thread pode falhar
  int create3 = 1;
  int create4 = 1;
  for(i = 0; i<iteracoes; i++ ){
   mpf_clear(a[i]);
   mpf_clear(b[i]);
   mpf_clear(t[i]);
   mpf_clear(p[i]);
   mpf_clear(pi[i]);
   abper[i] = 0;
   tper[i] = 0;
   pper[i] = 0;
 }

  bitsdeprecisao = mpf_get_default_prec();
  printf("Essa eh a precisao default %ld \n",bitsdeprecisao);
  printf("\nDebugando antes de criar a thread");   
  while( create1 != 0){
    create1 = pthread_create( &calcab, NULL, calculaab, NULL);
    printf("\nnao foi possivel criar a thread 1");
    mywait(2,5);
  }
  printf("\nthread criada e crete vale %d",create1);
  //mywait(1,&mtxsleep,&mtxcondsleep);
  while (create2 != 0 ){
    create2 = pthread_create( &calct, NULL, calculat, NULL);
    printf("\nnao foi possivel criar a thread 2");
    mywait(2,5);
  }
  printf("\nthread criada e crete vale %d",create2);
  //mywait(1,&mtxsleep,&mtxcondsleep);
  while( create3 != 0){
    create3 = pthread_create( &calcp, NULL, calculap, NULL);
    printf("\nnao foi possivel criar a thread 3");
    mywait(2,5);
  }
  printf("\nthread criada e crete vale %d",create3);
  while( create4 != 0){
    create4 = pthread_create( &calcpi, NULL, calculapi, NULL);
    printf("\nnao foi possivel criar a thread 4");
    mywait(1,5);
  }
  printf("\nthread criada e crete vale %d",create4);
  int returning;
  int itera;
  
  pthread_join(calcab,NULL);
  pthread_join(calct,NULL);
  pthread_join(calcp,NULL);
  pthread_join(calcpi,NULL);
 for(i = 0; i<iteracoes; i++ ){
   mpf_clear(a[i]);
   mpf_clear(b[i]);
   mpf_clear(t[i]);
   mpf_clear(p[i]);
   mpf_clear(pi[i]);
   abper[i] = 0;
   tper[i] = 0;
   pper[i] = 0;
 }
 fim = time(NULL);
 printf("\n programa completo com exito, e o tempo de execucao foi de: %.0f\n segundos\n",difftime(fim,inicio));
return 0;
}
